import 'dart:async';
import 'dart:math';

import 'package:audioplayers/audioplayers.dart';
import 'package:event_bus/event_bus.dart';
import 'package:flutter/services.dart';
import 'package:flutter_dva/flutter_dva.dart';
import 'package:todo_app/constants/audio.dart';
import 'package:todo_app/controllers/RtcRoomController.dart';
import 'package:todo_app/controllers/definitions/CreatedRtcRoomParams.dart';
import 'package:todo_app/controllers/definitions/RtcRoomModel.dart';
import 'package:todo_app/controllers/definitions/UserModel.dart';
import 'package:todo_app/models/getters.dart';
import 'package:todo_app/utils/call/CallFms.dart';
import 'package:todo_app/utils/call/contexts/ConnectingUIContext.dart';
import 'package:todo_app/utils/call/contexts/IdleContext.dart';
import 'package:todo_app/utils/call/contexts/TalkUIContext.dart';
import 'package:todo_app/utils/webrtc/RTCMessageEventBus.dart';
import 'package:todo_app/utils/webrtc/RTCMessageModel.dart';

extension RoomExt on RtcRoomModel {
  UserModel get callee {
    var user = users?.firstWhere((element) => element.id! != created_by!, orElse: ()=> UserModel()..id = created_by);
    return user ?? (UserModel()..id = created_by);
  }

  UserModel get caller {
    var user = users?.firstWhere((element) => element.id! == created_by!, orElse: ()=> UserModel()..id = created_by);
    return user ?? (UserModel()..id = created_by);
  }
}

class CallUIHelper {
  /// 发起拨打电话
  static String call(UserModel me, UserModel other,[String roomName = "普通通话"]) {
    var meUid = me.id!;
    var otherId = other.id!;
    var roomId = 'room_${DateTime.now().millisecondsSinceEpoch}_${Random().nextInt(1000).toString().padLeft(4, '0')}';
    var userIds = ([meUid, otherId]..sort((a, b)=> a -b)).toList();
    var params = CreatedRtcRoomParams()
      ..room_id = roomId
      ..room_name = roomName + userIds.join('_')
      ..user_ids = userIds.join(",");
    CallHelper.callOut(
      RtcRoomModel()
        ..room_id = roomId
        ..room_name = roomName + userIds.join('_')
        ..user_ids = userIds.join(",")
        ..created_by = meUid
        ..user_id = meUid
        ..owner = me
        ..users = [me, other]
    );
    RtcRoomController.createdUsingPost(params)
      .then((res) {
        if(res.status == 'success') {
          var room =  res.data!;
          CallHelper.connecting(room);
          CallHelper.currentRoom = room;
          CallHelper.sendRtcMsg(RTCMsgType.invite, roomId);
        } else {
          CallHelper.endCall(closeReason: CloseReason.NETWORK_ERROR,roomId: roomId, force: true);
        }
      }).onError((error, stackTrace) {
        CallHelper.endCall(closeReason: CloseReason.NETWORK_ERROR,roomId: roomId, force: true);
      });
    return roomId;
  }
  
  /// 拒接接听
  static void reject(String roomId) {
    // CallHelper
    CallHelper.endCall(closeReason: CloseReason.REJECT, roomId: roomId, force: true);
    // Server
    RtcRoomController.rejectUsingPut(roomId);
  }

  /// 接听
  static void accept(RtcRoomModel room) {
    // CallHelper
    CallHelper.connected(room);
    // Server
    RtcRoomController.acceptUsingPut(room.room_id!);
    // ICEServer
    CallHelper.sendRtcMsg(RTCMsgType.accept, room.room_id!);
  }

  /// 结束拨打
  static void handUp(String roomId) {
    // Server
    RtcRoomController.endUsingPut(roomId);
    // CallHelper
    CallHelper.endCall(closeReason: CloseReason.HAND_UP, roomId: roomId, force: true);
  }
}

class CallHelper {
  static bool isMini = false;
  static EventBus eventBus = EventBus();
  static CallFms callFms = CallFms();
  static RtcRoomModel? currentRoom;
  static StreamSubscription<RTCMessageResponse>? subscription;
  static AudioPlayer callMp3Player = AudioPlayer()..setSourceAsset(AssetAudios.call)..setReleaseMode(ReleaseMode.loop);
  static AudioCache audioCache = AudioCache();
  static Map<String, List<int?>> joinedUserIds = {};

  static void init() {
    callFms.init();
    initMqtt();
  }

  static initMqtt() {
    subscription = RTCMessageEventBus.eventBus.on<RTCMessageResponse>().asBroadcastStream().listen((event) {
      if(event.type == RTCMessageResponseType.receivedMsg) {
        var msg = event.payload;
        var roomId = msg.room!;
        switch (msg.type) {
          case RTCMsgType.invite:
            RtcRoomController.infoUsingGet(roomId).then((res) {
              if(res.status == "success") {
                CallHelper.callIn(res.data!);
                CallHelper.currentRoom = res.data!;
              } else {
                CallHelper.endCall(closeReason: CloseReason.NETWORK_ERROR, roomId: roomId);
              }
            });
            break;
          case RTCMsgType.accept:
            if(currentRoom?.room_id == roomId) {
              CallHelper.connected(currentRoom!);
            } else {
              CallHelper.endCall(closeReason: CloseReason.USER_BUSY, roomId: roomId);
            }
            break;
          case RTCMsgType.reject:
            CallHelper.endCall(closeReason: CloseReason.REJECT, roomId: roomId);
            break;
          case RTCMsgType.handUp:
            CallHelper.endCall(closeReason: CloseReason.HAND_UP, roomId: roomId);
            break;
          case RTCMsgType.exitRoom:
            if(joinedUserIds.containsKey(roomId)) {
              joinedUserIds[roomId]?.remove(msg.suid);
            }
            CallHelper.endCallOnly(closeReason: CloseReason.fromName(msg.payload), roomId: roomId);
            break;
          case RTCMsgType.joinRoom:
            if(!joinedUserIds.containsKey(roomId)) {
              joinedUserIds[roomId] = [];
            }
            joinedUserIds[roomId]?.add(msg.suid);
            break;
          default:
            break;
        }
      }
    });
  }

  static void dispose() {
    callFms.dispose();
    subscription?.cancel();
  }

  static void setAppState() {
    eventBus.fire(CallHelperEvent(
      type: CallHelperEventType.CHANGE_APP_SATE,
      payload: {}
    ));
  }

  static void setUiChange() {
        eventBus.fire(CallHelperEvent(
      type: CallHelperEventType.CHANGE_CALL_UI,
      payload: {}
    ));
  }

  static void callOut(RtcRoomModel room) {
    CallHelper.callFms.eventBus.fire(CallTransitionEvent(
      type: CallTransitionType.CALL_OUT,
      payload: ConnectingUIContext(requestSuccess: false, room: room)
    ));
  }

  static void callIn(RtcRoomModel room) {
    CallHelper.callFms.eventBus.fire(CallTransitionEvent(
      type: CallTransitionType.CALL_IN,
      payload: ConnectingUIContext(requestSuccess: true, room: room)
    ));
  }

  static void endCall({required CloseReason closeReason, required String roomId, bool force = false}) {
    CallHelper.callFms.eventBus.fire(CallTransitionEvent(
      type: CallTransitionType.DISCONNECTED,
      payload: IdleContext(
        closeReason: closeReason,
        chatRoomId: roomId, 
        force: force
      )
    ));
    sendRtcMsgByExit(roomId, closeReason);
  }

  static void endCallOnly ({required CloseReason closeReason, required String roomId}) {
    CallHelper.callFms.eventBus.fire(CallTransitionEvent(
      type: CallTransitionType.DISCONNECTED,
      payload: IdleContext(
        closeReason: closeReason,
        chatRoomId: roomId, 
        force: false
      )
    ));
  }

  static void connecting(RtcRoomModel room) {
    CallHelper.callFms.eventBus.fire(CallTransitionEvent(
      type: CallTransitionType.CONNECTING,
      payload: ConnectingUIContext(requestSuccess: true, room: room)
    ));
  }

  static void connected(RtcRoomModel room) {
    CallHelper.callFms.eventBus.fire(CallTransitionEvent(
      type: CallTransitionType.CONNECTED,
      payload: TalkUIContext(room: room)
    ));
  }

  static Future connectingFromState(RtcRoomModel? room) async {
    // 本地提示
    await callMp3Player.play(AssetSource(AssetAudios.call), volume: 1, mode: PlayerMode.lowLatency);
    HapticFeedback.heavyImpact();
  }

  static Future disposeConnectingFromState() async {
    // 停止
    await callMp3Player.stop();
  }

  static void enterIdleFromState() async {
    setUiChange();
    await callMp3Player.stop();
  }

  static void exitIdleFromState() { 
    setUiChange();
  }

  static void connectedFromState(RtcRoomModel? room) async {
    setUiChange();
    await callMp3Player.stop();
  }

  static void disconnectedFromState(String? chatRoomId) async {
    await Future.delayed(const Duration(milliseconds: 250));
    var audioPlayer = AudioPlayer()..setReleaseMode(ReleaseMode.stop);
    await audioPlayer.play(AssetSource(AssetAudios.callEnd), volume: 1, mode: PlayerMode.lowLatency, position: Duration.zero);
  }

  static void pushLocalMessageNotice() {}

  static void alertTimeout() {}

  static void forceEndCall() {}

  static void alertUserBusy() {}

  static void alertUserNotOnline() {}

  static void enableAndroidOverlays() {}
  
  static void initConnectingTimer(RtcRoomModel room) {}


  static int sendRtcMsgByExit(String roomId, CloseReason reason) {
    // Mqtt
    var sendUid = Provider().store?.rootState.system?.user?.id;
    if(sendUid == null) return - 1;
    return RTCMessageEventBus.sendRtcMessage(RTCMsgType.exitRoom, sendUid.toInt(), roomId, reason.name);
  }

  static int sendRtcMsg(RTCMsgType type, String roomId, [dynamic payload]) {
    // Mqtt
    var sendUid = Provider().store?.rootState.system?.user?.id;
    if(sendUid == null) return -1;
    return RTCMessageEventBus.sendRtcMessage(type, sendUid.toInt(), roomId, payload);
  }
}

enum CallHelperEventType {
  CHANGE_APP_SATE, // 更新APP状态
  CHANGE_CALL_UI, // 更新拨打界面
}

class CallHelperEvent {
  final CallHelperEventType type;
  final dynamic payload;

  CallHelperEvent({required this.type, this.payload});
}